using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Security;
using csammisrun.MsnLib.Utility;

namespace csammisrun.MsnLib
{
  public enum P2PMessageType
  {
    Invitation
  }

  public class P2PInfo
  {
    private UInt32 _msgid = 0;
    private UInt64 _totaldatasize = 0;
    private UInt64 _offset = 0;
    private string _dest = "";
    private string _nextstep = "";
    private string _callid = "";
    private Stream _stream = null;
    private string _sessiontype = "";
    private string _branchid = "";

    public P2PInfo(UInt32 msgid, UInt64 totaldatasize, UInt64 offset, string destination,
      string nextstep, string callid, Stream file, string sessiontype, string branchid)
    {
      _msgid = msgid;
      _totaldatasize = totaldatasize;
      _offset = offset;
      _dest = destination;
      _nextstep = nextstep;
      _callid = callid;
      _stream = file;
      _sessiontype = sessiontype;
      _branchid = branchid;
    }

    public UInt32 MsgID { get { return _msgid; } set { _msgid = value; } }
    public UInt64 TotalDataSize { get { return _totaldatasize; } set { _totaldatasize = value; } }
    public UInt64 Offset { get { return _offset; } set { _offset = value; } }
    public string Destination { get { return _dest; } set { _dest = value; } }
    public string NextStep { get { return _nextstep; } set { _nextstep = value; } }
    public string CallID { get { return _callid; } set { _callid = value; } }
    public Stream File { get { return _stream; } set { _stream = value; } }
    public string SessionType { get { return _sessiontype; } set { _sessiontype = value; } }
    public string BranchID { get { return _branchid; } set { _branchid = value; } }

    internal void GenerateMsgID()
    {
      _msgid = (uint)new Random().Next(1000000);
    }

    internal byte[] GenerateBinary(string originalsid, SlpHeader header)
    {
      uint osid = uint.Parse(originalsid);
      byte[] buffer = new byte[52];
      int i = 0, index = 0;
      ulong mask = 0xFF;

      for (i = 0, mask = 0xFF; i < 4; i++, index++, mask <<= 8)
      {
        buffer[index] = 0;
      }
      for (i = 0, mask = 0xFF; i < 4; i++, index++, mask <<= 8)
      {
        buffer[index] = (byte)((osid & mask) >> (i * 8));
      }
      for (i = 0, mask = 0xFF; i < 4; i++, index++, mask <<= 8)
      {
        buffer[index] = (byte)((_msgid & mask) >> (i * 8));
      }
      for (i = 0, mask = 0xFF; i < 8; i++, index++, mask <<= 8)
      {
        buffer[index] = 0;
      }
      for (i = 0, mask = 0xFF; i < 8; i++, index++, mask <<= 8)
      {
        buffer[index] = (byte)((_totaldatasize & mask) >> (i * 8));
      }
      for (i = 0, mask = 0xFF; i < 4; i++, index++, mask <<= 8)
      {
        buffer[index] = 0;
      }
      for (i = 0, mask = 0xFF; i < 4; i++, index++, mask <<= 8)
      {
        buffer[index] = (byte)((0x02 & mask) >> (i * 8));
      }
      for (i = 0, mask = 0xFF; i < 4; i++, index++, mask <<= 8)
      {
        buffer[index] = (byte)((header.SessionID & mask) >> (i * 8));
      }
      for (i = 0, mask = 0xFF; i < 4; i++, index++, mask <<= 8)
      {
        buffer[index] = (byte)((header.AckID & mask) >> (i * 8));
      }
      return buffer;
    }
  }

  public class P2PMessage
  {
    private static Dictionary<string, P2PInfo> _sessions = new Dictionary<string, P2PInfo>();
    public static P2PInfo GetInfoBySID(string sid)
    {
      if (_sessions.ContainsKey(sid))
      {
        return _sessions[sid];
      }
      return null;
    }

    public static void SetInfoBySID(string sid, P2PInfo info)
    {
      _sessions[sid] = info;
    }

    public static void RemoveInfoBySID(string sid)
    {
      if (_sessions.ContainsKey(sid))
      {
        _sessions.Remove(sid);
      }
    }

    private const string BUDDY_OR_EMOT = "A4268EEC-FEC5-49E5-95C3-F126696BDBF6";
    private const string FILETRANSFER = "5D3E02AB-6190-11D3-BBBB-00C04F795683";

    private SlpHeader _header;
    private Switchboard _parent = null;
    private string _data = "";
    private P2PMessageType _messagetype;
    private User _remote = null;

    public P2PMessage(Switchboard parent, Message template)
    {
      if(template.IsSLPMessage == false)
      {
        throw new ArgumentException("Message is not an SLPMessage", "template");
      }

      _parent = parent;
      _header = template.Header;
      _remote = _parent.GetSwitchboardUser(template.RemoteUser);
      _data = Encoding.UTF8.GetString(template.Body);
    }

    #region Methods
    /// <summary>
    /// Processes the P2P message data to retrieve its type and information
    /// </summary>
    public bool ProcessMessage()
    {
      if (Header.SessionID == 0 && Header.Length != 0 && Header.Length != Header.TotalSize)
      {
      }

      string sessid = "", destination = "", branchuid = "", uid = "", cseq = "", ctype = "";

      if (_data.IndexOf("INVITE MSNMSGR") != -1)
      {
        _messagetype = P2PMessageType.Invitation;
        sessid = GetP2PMessageAttribute("SessionID: ", false);
        destination = GetP2PMessageAttribute("From: <msnmsgr:", true);
        branchuid = GetP2PMessageAttribute("branch={", true);
        uid = GetP2PMessageAttribute("Call-ID: {", true);
        cseq = GetP2PMessageAttribute("CSeq: ", false);
        ctype = GetP2PMessageAttribute("Content-Type: ", false);

        if (ctype == "application/x-msnmsgr-transreqbody")
        {
        }
        else if (ctype == "application/x-msnmsgr-sessionreqbody")
        {
          string eufguid = GetP2PMessageAttribute("EUF-GUID: {", true);
          string context = GetP2PMessageAttribute("Context: ", false);

          switch (eufguid)
          {
            case FILETRANSFER:
              // Store the info
              P2PMessage.SetInfoBySID(sessid, new P2PInfo(0, 0, 0, destination, "", uid, null, "filetransfer", branchuid));

              int index = 0;
              uint size = 0, filesize = 0, nopreview = 1;
              string filename = "";
              byte[] contextarray = null;
              byte[] previewarray = null;
              System.IO.MemoryStream previewimg = null;

              // Pad out the context so FromBase64String can deal with it
              while (context.Length % 4 != 0)
              {
                context += "=";
              }
              contextarray = Convert.FromBase64String(context);
              // Extract the relevant data from the context
              size = Marshal.ByteArrayToUint32(contextarray, ref index);
              index = 8;
              filesize = Marshal.ByteArrayToUint32(contextarray, ref index);
              index = 16;
              nopreview = Marshal.ByteArrayToUint32(contextarray, ref index);
              filename = Encoding.Unicode.GetString(contextarray, 20, contextarray.Length - 20);
              if (filename.IndexOf("\u0000") != -1)
              {
                filename = filename.Substring(0, filename.IndexOf("\u0000"));
              }

              if (nopreview == 0)
              {
                // A small preview image (PNG) was sent along with this transfer
                previewarray = new byte[contextarray.Length - size];
                Array.ConstrainedCopy(contextarray, (int)size, previewarray, 0, previewarray.Length);
                previewimg = new System.IO.MemoryStream(previewarray);
              }

              _parent.SendFileTransferAck(sessid);

              _parent.ParentSession.OnFileTransferInvitationReceived(_parent.GetSwitchboardUser(destination),
                filename, (int)filesize, sessid, previewimg);
              break;
            default:
              Logging.WriteString("Unhandled P2P EUFGUID: " + eufguid);
              return false;
          }
        }
      }

      return true;
    }

    private string GetP2PMessageAttribute(string key, bool striplast)
    {
      int index = _data.IndexOf(key);
      if (index == -1)
      {
        return "";
      }

      index += key.Length;
      int endindex = _data.IndexOf("\r\n", index);
      if (endindex == -1)
      {
        endindex = _data.Length;
      }
      return _data.Substring(index, endindex - index - ((striplast) ? 1 : 0));
    }
    #endregion

    #region Properties
    /// <summary>
    /// Gets the <see cref="SlpHeader"/> of this message
    /// </summary>
    internal SlpHeader Header
    {
      get { return _header; }
    }
    #endregion
  }
}
